package com.pactera.asmp.server.utils;

import com.pactera.asmp.server.config.MinioConfig;
import io.minio.*;
import io.minio.http.Method;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
import java.util.UUID;

/**
 * @ProjectName: tlsserver
 * @Package com.pactera.asmp.server.utils
 * @ClassName: MinioUtils
 * @Description: minio 工具类
 * @Date 2020/06/08 17:29
 * @Author lvzhiqiang
 * @Version 1.0
 */
@Component
@Slf4j
public class MinioUtils {
    private final String POLICY_CONFIG = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetBucketLocation\",\"s3:ListBucket\"],\"Resource\":[\"arn:aws:s3:::#bucketName#\"]},{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetObject\"],\"Resource\":[\"arn:aws:s3:::#bucketName#/*\"]}]}";

    private MinioClient minioClient;

    private MinioConfig minioConfig;

    /**
     * 禁用默认构造器
     */
    private MinioUtils() {}
    @Autowired
    public MinioUtils(MinioConfig minioConfig) throws Exception {
        this.minioConfig = minioConfig;
        minioClient();
    }

    /**
     * 上传文件
     * @param file
     * @return 新文件名
     * @throws Exception
     */
    public String uploadFile(MultipartFile file) throws Exception {
        // 判断上传文件是否为空
        if (null == file || 0 == file.getSize()) {
            log.error("上传文件不能为空");
            throw new RuntimeException("上传文件不能为空");
        }
        // 原始文件名
        String originalFilename = file.getOriginalFilename();
        log.info("uploadFile originalFilename: {}", originalFilename);

        // 新的文件名 = 原始文件_UUID.后缀名
        String []fileNames = originalFilename.split("\\.");
        String suffix = "";
        int length = 2;
        if (length == fileNames.length) {
            suffix = "." + fileNames[1];
        }
        String newFileName = fileNames[0] + "_"
                + UUID.randomUUID().toString().replaceAll("-", "") + suffix;
        // 开始上传
        minioClient.putObject(
                PutObjectArgs.builder()
                        .bucket(minioConfig.getBucketName())
                        .object(newFileName)
                        .contentType(file.getContentType())
                        .stream(file.getInputStream(), file.getSize(), -1)
                        .build());
        return newFileName;
    }

    /**
     * 上传文件
     * @param is 文件流
     * @param fileName 原始文件名，带后缀
     * @param contentType 文件类型，不传默认二进制流
     * @return
     * @throws Exception
     */
    public String uploadFile(InputStream is, String fileName, String contentType) throws Exception {
        // 判断上传文件流是否为空
        if (null == is) {
            log.error("上传文件流不能为空");
            throw new RuntimeException("上传文件流不能为空");
        }
        // 原始文件名
        log.info("uploadFile fileName: {}, contentType: {}", fileName, contentType);

        // 新的文件名 = 原始文件_UUID.后缀名
        String []fileNames = fileName.split("\\.");
        String suffix = "";
        int length = 2;
        if (length == fileNames.length) {
            suffix = "." + fileNames[1];
        }
        String newFileName = fileNames[0] + "_"
                + UUID.randomUUID().toString().replaceAll("-", "") + suffix;
        // 开始上传
        minioClient.putObject(
                PutObjectArgs.builder()
                        .bucket(minioConfig.getBucketName())
                        .object(newFileName)
                        .contentType(StringUtils.isNotBlank(contentType) ? contentType : "application/octet-stream")
                        .stream(is, is.available(), -1)
                        .build());
        return newFileName;
    }

    /**
     * 获取浏览地址
     * @param fileName
     * @param expiry 过期时间，最长7天
     * @return
     * @throws Exception
     */
    public String getPresignedObjectUrl(String fileName, int expiry) throws Exception {
        log.info("getPresignedObjectUrl fileName: {}, expiry: {}", fileName, expiry);
        if (StringUtils.isBlank(fileName) || expiry <= 0) {
            throw new RuntimeException("参数不能为空");
        }
        String url =
                minioClient.getPresignedObjectUrl(
                        GetPresignedObjectUrlArgs.builder()
                                .method(Method.GET)
                                .bucket(minioConfig.getBucketName())
                                .object(fileName)
                                .expiry(expiry)
                                .build());
        return url;
    }

    /**
     * 下载文件
     * @param fileName
     * @return 字节数组
     */
    public byte[] getObjectByte(String fileName) throws Exception {
        log.info("getObject fileName: {}", fileName);
        if (StringUtils.isBlank(fileName)) {
            throw new RuntimeException("参数不能为空");
        }
        byte[] buff;
        try (InputStream stream = minioClient.getObject(
                GetObjectArgs.builder()
                        .bucket(minioConfig.getBucketName())
                        .object(fileName)
                        .build())) {
            buff = IOUtils.toByteArray(stream);
            IOUtils.closeQuietly(stream);
            return buff;
        } catch (Exception e) {
            log.error("getObject error: ", e);
            throw e;
        }
    }

    /**
     * 下载文件
     * @param fileName
     * @return 流
     */
    public InputStream getObjectInputStream(String fileName) throws Exception {
        log.info("getObject fileName: {}", fileName);
        if (StringUtils.isBlank(fileName)) {
            throw new RuntimeException("参数不能为空");
        }
        try  {
            InputStream is = minioClient.getObject(
                    GetObjectArgs.builder()
                            .bucket(minioConfig.getBucketName())
                            .object(fileName)
                            .build());
            return is;
        } catch (Exception e) {
            log.error("getObject error: ", e);
            throw e;
        }
    }

    /**
     * 删除文件
     * @param fileName
     * @throws Exception
     */
    public void removeObject(String fileName) throws Exception {
        log.info("removeObject fileName: {}", fileName);
        if (StringUtils.isBlank(fileName)) {
            throw new RuntimeException("参数不能为空");
        }
        minioClient.removeObject(
                RemoveObjectArgs.builder()
                        .bucket(minioConfig.getBucketName())
                        .object(fileName).build());
    }

    /**
     * 创建客户端实体
     * @return
     * @throws Exception
     */
    private void minioClient() throws Exception {
        this.checkParam();
        this.minioClient =
                MinioClient.builder()
                        .endpoint(this.minioConfig.getUrl())
                        .credentials(this.minioConfig.getAccessKey(), this.minioConfig.getSecretKey())
                        .build();
        // 当前 bucket 不存在时即创建
        if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(this.minioConfig.getBucketName()).build())) {
            minioClient.makeBucket(MakeBucketArgs.builder()
                    .bucket(this.minioConfig.getBucketName())
                    .build());
        }

        // 当前 bucket 无权限时，默认开启读权限
        String policy = minioClient.getBucketPolicy(GetBucketPolicyArgs.builder()
                .bucket(this.minioConfig.getBucketName()).build());
        if (StringUtils.isBlank(policy)) {
            String config = POLICY_CONFIG.replaceAll("#bucketName#", this.minioConfig.getBucketName());
            this.setBucketPolicy(config);
        }
    }

    /**
     * 配置参数校验
     * @throws RuntimeException
     */
    private void checkParam() throws RuntimeException{
        if (StringUtils.isBlank(this.minioConfig.getUrl())
                || StringUtils.isBlank(this.minioConfig.getAccessKey())
                || StringUtils.isBlank(this.minioConfig.getSecretKey())
                || StringUtils.isBlank(this.minioConfig.getBucketName())) {
            throw new RuntimeException("配置参数不能为空");
        }
    }

    /**
     * 给 bucket 添加权限
     * @throws Exception
     */
    public void setBucketPolicy(String config) throws Exception {
        minioClient.setBucketPolicy(SetBucketPolicyArgs.builder()
                    .bucket(this.minioConfig.getBucketName()).config(config).build());
    }

    /**
     * 获取桶名
     * @return
     */
    public String getBucketName() {
        return this.minioConfig.getBucketName();
    }
}
